package com.jeasonzhao.web;

import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.ConvertEx;
import javax.servlet.jsp.PageContext;
import java.net.URLEncoder;
import java.io.*;

public abstract class BasicTagSupport extends BodyTagSupport
{
    private static final long serialVersionUID = 1L;
    private static final String INCLUDECSS = "<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\"></link>";
    private static final String INCLUDESCRIPT = "<script type=\"text/javascript\" src=\"{0}\"></script>";
    public static final String DEFAULT_DATA_ATTRIBUTE = "dataAttribute";

    private String dataAttribute = DEFAULT_DATA_ATTRIBUTE;
    private IWebContextWrapper wrapper = null;
    protected boolean isUsingBodyWriter = false;
    public BasicTagSupport()
    {
        super();
    }

    public BasicTagSupport setWebContextWrapper(IWebContextWrapper wr)
    {
        this.wrapper = wr;
        return this;
    }

    public IWebContextWrapper createWrapper()
    {
        return new WebContextWrapper(this,this.pageContext,this.isUsingBodyWriter);
    }

    public String getContextPath()
    {
        return pageContext == null || pageContext.getRequest() == null
            ? "/"
            : ((javax.servlet.http.HttpServletRequest) pageContext.getRequest()).getContextPath();
    }

    public String getAbsoluteUrl(String str)
    {
        if(str == null)
        {
            return str;
        }
        else
        {
            String contextPath = this.getContextPath();
            if(str.startsWith("/") && str.startsWith(contextPath) == false)
            {
                return(contextPath + str)
                    .replaceAll("//","/")
                    .replaceAll("//","/")
                    .replaceAll("//","/");
            }
            else
            {
                return str
                    .replaceAll("//","/")
                    .replaceAll("//","/")
                    .replaceAll("//","/");
            }
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T getParameter(String strKey)
    {
        return(T) (null == wrapper
                   ? ServletHelper.getParameter((null == pageContext ? null : pageContext.getRequest()),strKey)
                   : wrapper.get(strKey));
    }

    public int getParameterInt(String strKey)
    {
        return ConvertEx.toInt(this.getParameter(strKey));
    }

    public int getParameterInt(String strKey,int defaultValue)
    {
        return ConvertEx.toInt(this.getParameter(strKey),defaultValue);
    }

    public double getParameterDouble(String strKey)
    {
        return ConvertEx.toDouble(this.getParameter(strKey));
    }

    public double getParameterDouble(String strKey,double defaultValue)
    {
        return ConvertEx.toDouble(this.getParameter(strKey),defaultValue);
    }

    public java.util.Date getParameterDate(String strKey)
    {
        return ConvertEx.toDate(this.getParameter(strKey));
    }
    public boolean checkAndSetRequestAttribute(String id,Object v)
    {
        if(null!=this.pageContext && null!=this.pageContext.getRequest())
        {
            boolean b = this.pageContext.getRequest().getAttribute(id)==null;
            this.pageContext.getRequest().setAttribute(id,v);
            return b;
        }
        return false;
    }
    public void writeScript(CharSequence strScriptContent,boolean isDefer)
    {
        if(Algorithms.notEmpty(strScriptContent))
        {
            String script = ("<script type=\"text/javascript\" " + (isDefer ? "defer" : "") + ">"
                             + "\r\n"
                             + JSBeautify.beautify(strScriptContent.toString())
                             + "\r\n"
                             + "</script>")
                .replaceAll("\\n[\\s| ]*\\r","");
            this.writeln(script);
        }
    }

    public void writeScript(String strScriptContent)
    {
        writeScript(strScriptContent,false);
    }

    public void writelnByPos(String str,Object ...parameters)
    {
        this.writeln(Algorithms.replaceStringByPosition(str,parameters));
    }

    public void writelnByName(String str,Object ...parameters)
    {
        this.writeln(Algorithms.replaceStringByName(str,parameters));
    }

    public void writeScriptInclude(String ...scripts)
    {
        if(null == scripts)
        {
            return;
        }
        else
        {
            for(String s : scripts)
            {
                if(false == Algorithms.isEmpty(s))
                {
                    this.writelnByPos(INCLUDESCRIPT,getAbsoluteUrl(s));
                }
            }
        }
    }

    public void writeCSSInclude(String ...cssFile)
    {
        if(null == cssFile)
        {
            return;
        }
        else
        {
            for(String s : cssFile)
            {
                if(false == Algorithms.isEmpty(s))
                {
                    this.writelnByPos(INCLUDECSS,getAbsoluteUrl(s));
                }
            }
        }
    }

    public BasicTagSupport writeln(CharSequence str)
    {
        if(null != str)
        {
            write(str);
            write("\r\n");
        }
        return this;
    }

    public void write(CharSequence str)
    {
        if(str == null||str.length()<1)
        {
            return;
        }
        else if(null != wrapper && null != wrapper.getWriter())
        {
            try
            {
                wrapper.getWriter().write(str);
            }
            catch(java.io.IOException e)
            {

            }
        }
        else
        {
            try
            {
                BodyContent body = getBodyContent();
                if(body == null || false == isUsingBodyWriter)
                {
                    this.pageContext.getOut().write(str.toString());
                    this.pageContext.getOut().flush();
                }
                else
                {
                    JspWriter out = body.getEnclosingWriter();
                    out.write(str.toString());
                    out.flush();
                }
            }
            catch(Exception excep)
            {
                //excep.printStackTrace();
                //do not handle exceptions at here.
            }
        }
    }

    public void write(StringBuffer buf)
    {
        if(null != buf)
        {
            write(buf.toString());
        }
    }

    public void clientRedirectTo(String strUrl)
    {
        write("<script type=\"text/javascript\">window.location=\"" + this.getAbsoluteUrl(strUrl) + "\";</script>");
    }

//    public static void debug(Object str)
//    {
//        Throwable t = new Throwable();
//        StackTraceElement e = null;
//        StackTraceElement emy = null;
//        StringBuffer stack = new StringBuffer();
//        String date = Algorithms.toString(new java.util.Date());
//        stack.append("[TRACE INFO " + date + "] ");
//        for(StackTraceElement es : t.getStackTrace())
//        {
//            if(es.getClassName().startsWith("java.")
//               || es.getClassName().startsWith("javax.")
//               || es.getClassName().startsWith("org.apache.")
//               || es.getClassName().startsWith("org.springframework.")
//               || es.getClassName().startsWith("net.sf.")
//               || es.getClassName().startsWith("org.hibernate.")
//               || es.getClassName().startsWith("com.opensymphony.")
//                )
//            {
//                continue;
//            }
//            else
//            {
//                if(es.getClassName().startsWith(BasicTagSupport.class.getPackage().getName() + ".") == false)
//                {
//                    e = es;
//                }
//                else
//                {
//                    emy = es;
//                }
//            }
//        }
//        if(e == null)
//        {
//            e = emy;
//        }
//        if(e != null)
//        {
//            stack.append(e.getClassName() + ":" + e.getLineNumber() + "[" + e.getMethodName() + "] ");
//        }
//        stack.append(Algorithms.toString(str).trim());
//        System.out.println(stack);
//    }
    public static String generateURL(String url,String...parameters)
    {
        if(Algorithms.isEmpty(url))
        {
            return null;
        }
        else
        {
            String ret="";
            for(int n=0;null!=parameters && (n+1)<parameters.length;n+=2)
            {
                String key= parameters[n];
                String value=parameters[n+1];
                if(Algorithms.notEmpty(key) && Algorithms.notEmpty(value))
                {
                    if(ret.length()>0)
                    {
                        ret+="&";
                    }
                    try
                    {
                        ret += key.trim() + "=" + URLEncoder.encode(value,"UTF-8");
                    }
                    catch(UnsupportedEncodingException ex)
                    {
                        ret += key.trim() + "=" + value.trim();
                    }
                }
            }
            if(ret.length()>0)
            {
                ret=url+"?"+ret;
                return ret;
            }
            else
            {
                return url;
            }
        }
    }
    protected void cleanEmbeddedData()
    {
        localSettingEmbeddedData = null;
    }

    private Object localSettingEmbeddedData = null;
    public void setEmbeddedData(Object obj)
    {
        localSettingEmbeddedData = obj;
    }

    public Object getEmbeddedData()
    {
        if(null != localSettingEmbeddedData)
        {
            return localSettingEmbeddedData;
        }
        if(null != this.wrapper)
        {
            return this.wrapper.get(Algorithms.oneOfNotNull(this.dataAttribute,DEFAULT_DATA_ATTRIBUTE));
        }
        else if(null != this.pageContext)
        {
            return this.pageContext.getRequest().getAttribute(Algorithms.oneOfNotNull(this.dataAttribute,DEFAULT_DATA_ATTRIBUTE));
        }
        else
        {
            return null;
        }
    }

    public String getDataAttribute()
    {
        return this.dataAttribute;
    }

    public void setDataAttribute(String d)
    {
        this.dataAttribute = d;
    }

    public String simulate()
        throws Exception
    {
        return simulate((IWebContextWrapper)null,null);
    }

    public String simulate(PageContext context,Object embdata)
        throws Exception
    {
        return simulate(new WebContextWrapper(context),embdata);
    }

    public String simulate(PageContext context,IHtmlWriter w,Object embdata)
        throws Exception
    {
        return simulate(new WebContextWrapper(context,w),embdata);
    }

    public String simulate(PageContext context)
        throws Exception
    {
        return simulate(new WebContextWrapper(context),null);
    }

    public String simulate(PageContext context,IHtmlWriter w)
        throws Exception
    {
        return simulate(new WebContextWrapper(context,w),null);
    }

    public String simulate(Object embdata)
        throws Exception
    {
        return simulate((IWebContextWrapper)null,embdata);
    }

    public String simulate(IWebContextWrapper w,Object embdata)
        throws Exception
    {
        if(null == w)
        {
            w = new IWebContextWrapper.StringWrapper();
        }
        IWebContextWrapper oldw=this.wrapper;
        Object oldvalue=this.localSettingEmbeddedData;
        try
        {
            this.wrapper = w;
            this.localSettingEmbeddedData=embdata;
            this.doStartTag();
            this.doInitBody();
            this.doAfterBody();
            this.doEndTag();
            return w.toString();
        }
        catch(Exception ex)
        {
            throw ex;
        }
        finally
        {
            this.wrapper = oldw;
            this.localSettingEmbeddedData=oldvalue;
        }
    }

}
